// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package cipher

import (
	subtleoverlap "crypto/internal/subtle"
	"crypto/subtle"
	"encoding/binary"
	"errors"
)

// AEAD是一种密码模式，为相关的
// 数据提供经过身份验证的加密。有关方法的说明，请参见
// en.wikipedia.org/wiki/Authenticated_encryption
type AEAD interface {
	// NonceSize返回必须传递给Seal 
	// 并打开的nonce的大小。
	NonceSize() int

	// 开销返回
	// 明文与其密文之间的最大长度差。
	Overhead() int

	// Seal对明文进行加密和身份验证，对
	// 附加数据进行身份验证，并将结果附加到dst，返回更新后的
	// 切片。对于给定的密钥，nonce必须为NonceSize（）字节长，并且对于所有
	// 时间都是唯一的。
	// 
	// 要重新使用明文存储进行加密输出，请使用明文[：0]
	// 作为dst。否则，dst的剩余容量不得与明文重叠。
	Seal(dst, nonce, plaintext, additionalData []byte) []byte

	// Open对密文进行解密和身份验证，对
	// 附加数据进行身份验证，如果成功，将生成的明文
	// 附加到dst，并返回更新的片段。nonce必须是NonceSize（）
	// 字节长，并且它和附加数据必须与传递给Seal的
	// 值匹配。
	// 
	// 要重新使用密文存储进行解密输出，请使用密文[：0]
	// 作为dst。否则，dst的剩余容量不得与明文重叠。
	// 
	// 即使函数失败，dst的内容也可能被覆盖，直至其容量达到
	// 为止。
	Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error)
}

// gcmAble是一个由密码实现的接口，密码具有特定的优化
// GCM实现，如crypto/aes。NewGCM将检查此接口
// 如果找到，将返回特定的AEAD。
type gcmAble interface {
	NewGCM(nonceSize, tagSize int) (AEAD, error)
}

// gcmFieldElement表示一个值，单位为GF（2）⁸). 以反映GCM 
// /标准并制作二进制文件。BigEndian适合于封送这些值，
// 位以BigEndian顺序存储。例如：
// x的系数⁰ 可通过v.low>>63获得。
// x的系数⁶³可通过v.low和1获得。
// x的系数⁶⁴ 可通过v.high>>63获得。
// x的系数⁷ 可通过v.high和1获得。
type gcmFieldElement struct {
	low, high uint64
}

// gcm代表具有特定密钥的伽罗瓦计数器模式。见
// https:
type gcm struct {
	cipher    Block
	nonceSize int
	tagSize   int
	// productTable包含密钥的前16次幂，H。
	// 但是，它们的顺序是位相反的。请参阅NewGCMWithNonsize。
	productTable [16]gcmFieldElement
}

// NewGCM返回给定的128位分组密码，该分组密码采用Galois计数器模式
// 和标准的nonce长度。
// 
// 一般来说，GCM执行的GHASH操作不是固定时间。
// 由aes创建基础块是一个例外。NewCipher 
// 在支持AES硬件的系统上。有关详细信息，请参阅crypto/aes软件包文档。
func NewGCM(cipher Block) (AEAD, error) {
	return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, gcmTagSize)
}

// NewGCMWithNonceSize返回给定的128位分组密码，该分组密码用Galois 
// 计数器模式包装，该模式接受给定长度的nonce。长度不能为零。
// 
// 仅当您需要与现有
// 使用非标准nonce长度的密码系统兼容时，才使用此函数。所有其他用户都应该使用
// NewGCM，它更快，更能防止误用。
func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
	return newGCMWithNonceAndTagSize(cipher, size, gcmTagSize)
}

// NewGCMWithTagSize返回给定的128位分组密码，用Galois 
// 计数器模式包装，该模式生成具有给定长度的标记。
// 
// 允许标记大小在12到16字节之间。
// 
// 仅当您需要与使用非标准标记长度的现有
// 密码系统兼容时，才使用此函数。所有其他用户都应该使用
// NewGCM，它更能抵抗误用。
func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) {
	return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, tagSize)
}

func newGCMWithNonceAndTagSize(cipher Block, nonceSize, tagSize int) (AEAD, error) {
	if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize {
		return nil, errors.New("cipher: incorrect tag size given to GCM")
	}

	if nonceSize <= 0 {
		return nil, errors.New("cipher: the nonce can't have zero length, or the security of the key will be immediately compromised")
	}

	if cipher, ok := cipher.(gcmAble); ok {
		return cipher.NewGCM(nonceSize, tagSize)
	}

	if cipher.BlockSize() != gcmBlockSize {
		return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
	}

	var key [gcmBlockSize]byte
	cipher.Encrypt(key[:], key[:])

	g := &gcm{cipher: cipher, nonceSize: nonceSize, tagSize: tagSize}

	// 我们预先计算了16倍的| key |。然而，当我们在这个表中查找
	// 时，我们将使用来自字段元素和
	// 的位，因此这些位的顺序将相反。所以通常一个
	// 会期望，比如说，4*键在表的索引4中，但是由于
	// 这个位的顺序，它实际上会在索引0010（基2）=2中。
	x := gcmFieldElement{
		binary.BigEndian.Uint64(key[:8]),
		binary.BigEndian.Uint64(key[8:]),
	}
	g.productTable[reverseBits(1)] = x

	for i := 2; i < 16; i += 2 {
		g.productTable[reverseBits(i)] = gcmDouble(&g.productTable[reverseBits(i/2)])
		g.productTable[reverseBits(i+1)] = gcmAdd(&g.productTable[reverseBits(i)], &x)
	}

	return g, nil
}

const (
	gcmBlockSize         = 16
	gcmTagSize           = 16
	gcmMinimumTagSize    = 12 // NIST SP 800-38D建议使用12字节或更多字节的标签。
	gcmStandardNonceSize = 12
)

func (g *gcm) NonceSize() int {
	return g.nonceSize
}

func (g *gcm) Overhead() int {
	return g.tagSize
}

func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
	if len(nonce) != g.nonceSize {
		panic("crypto/cipher: incorrect nonce length given to GCM")
	}
	if uint64(len(plaintext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize()) {
		panic("crypto/cipher: message too large for GCM")
	}

	ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
	if subtleoverlap.InexactOverlap(out, plaintext) {
		panic("crypto/cipher: invalid buffer overlap")
	}

	var counter, tagMask [gcmBlockSize]byte
	g.deriveCounter(&counter, nonce)

	g.cipher.Encrypt(tagMask[:], counter[:])
	gcmInc32(&counter)

	g.counterCrypt(out, plaintext, &counter)

	var tag [gcmTagSize]byte
	g.auth(tag[:], out[:len(plaintext)], data, &tagMask)
	copy(out[len(plaintext):], tag[:])

	return ret
}

var errOpen = errors.New("cipher: message authentication failed")

func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
	if len(nonce) != g.nonceSize {
		panic("crypto/cipher: incorrect nonce length given to GCM")
	}
	// 例如，如果实现
	// 未初始化标记大小，则进行健全性检查以防止身份验证始终成功。
	if g.tagSize < gcmMinimumTagSize {
		panic("crypto/cipher: incorrect GCM tag size")
	}

	if len(ciphertext) < g.tagSize {
		return nil, errOpen
	}
	if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize())+uint64(g.tagSize) {
		return nil, errOpen
	}

	tag := ciphertext[len(ciphertext)-g.tagSize:]
	ciphertext = ciphertext[:len(ciphertext)-g.tagSize]

	var counter, tagMask [gcmBlockSize]byte
	g.deriveCounter(&counter, nonce)

	g.cipher.Encrypt(tagMask[:], counter[:])
	gcmInc32(&counter)

	var expectedTag [gcmTagSize]byte
	g.auth(expectedTag[:], ciphertext, data, &tagMask)

	ret, out := sliceForAppend(dst, len(ciphertext))
	if subtleoverlap.InexactOverlap(out, ciphertext) {
		panic("crypto/cipher: invalid buffer overlap")
	}

	if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
		// AESNI代码同时进行解密和身份验证，如果标签不匹配，
		// 将覆盖dst。为了在
		// 的行为。
		// 平台上保持一致，这里模拟了
		for i := range out {
			out[i] = 0
		}
		return nil, errOpen
	}

	g.counterCrypt(out, ciphertext, &counter)

	return ret, nil
}

// 反转EBITS反转i中4位数字的位顺序。
func reverseBits(i int) int {
	i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
	i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
	return i
}

// gcmAdd添加GF（2）的两个元素⁸) 并返回总和。
func gcmAdd(x, y *gcmFieldElement) gcmFieldElement {
	// 特征2字段中的加法就是XOR。
	return gcmFieldElement{x.low ^ y.low, x.high ^ y.high}
}

// gcmDouble返回将GF（2）元素加倍的结果⁸). 
func gcmDouble(x *gcmFieldElement) (double gcmFieldElement) {
	msbSet := x.high&1 == 1

	// 由于位顺序，加倍实际上是右移。
	double.high = x.high >> 1
	double.high |= x.low << 63
	double.low = x.low >> 1

	// 如果在移位之前设置了最高有效位，则从概念上讲，它就是x^128的一个术语。这比
	// 不可约多项式大，因此结果必须减少。不可约多项式是1+x+x^2+x^7+x^128。我们可以把它减去
	// 去掉x^128处的项，这也意味着减去其他
	// 四项。在特征2字段中，减法==加法==
	// XOR。
	if msbSet {
		double.low ^= 0xe100000000000000
	}

	return
}

var gcmReductionTable = []uint16{
	0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
	0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
}

// mul将y设置为y*H，其中H是GCM密钥，在newGCMWithNonsize期间固定。
func (g *gcm) mul(y *gcmFieldElement) {
	var z gcmFieldElement

	for i := 0; i < 2; i++ {
		word := y.high
		if i == 1 {
			word = y.low
		}

		// 乘法的工作原理是将z乘以16，再加上
		// H的一个预计算倍数。
		for j := 0; j < 64; j += 4 {
			msw := z.high & 0xf
			z.high >>= 4
			z.high |= z.low << 60
			z.low >>= 4
			z.low ^= uint64(gcmReductionTable[msw]) << 48

			// 表|中的值按
			// 小尾数位位置排序。请参阅NewGCMWithNonsize中的注释
			// 。
			t := &g.productTable[word&0xf]

			z.low ^= t.low
			z.high ^= t.high
			word >>= 4
		}
	}

	*y = z
}

// updateBlocks根据
// Horner规则，从块中用更多多项式项扩展y。块中必须有多个gcmBlockSize字节。
func (g *gcm) updateBlocks(y *gcmFieldElement, blocks []byte) {
	for len(blocks) > 0 {
		y.low ^= binary.BigEndian.Uint64(blocks)
		y.high ^= binary.BigEndian.Uint64(blocks[8:])
		g.mul(y)
		blocks = blocks[gcmBlockSize:]
	}
}

// 更新使用数据中的更多多项式项扩展y。如果数据不是gcmBlockSize字节长的
// 倍数，则剩余部分为零填充。
func (g *gcm) update(y *gcmFieldElement, data []byte) {
	fullBlocks := (len(data) >> 4) << 4
	g.updateBlocks(y, data[:fullBlocks])

	if len(data) != fullBlocks {
		var partialBlock [gcmBlockSize]byte
		copy(partialBlock[:], data[fullBlocks:])
		g.updateBlocks(y, partialBlock[:])
	}
}

// gcmInc32将计数器块的最后四个字节视为一个大端值
// 并将其递增。
func gcmInc32(counterBlock *[16]byte) {
	ctr := counterBlock[len(counterBlock)-4:]
	binary.BigEndian.PutUint32(ctr, binary.BigEndian.Uint32(ctr)+1)
}

// sliceForAppend获取一个切片和请求的字节数。它返回一个
// 切片，其中给定切片的内容后跟那么多字节，以及一个
// 第二个切片，该切片化名为该切片，只包含额外的字节。如果
// 原始片有足够的容量，则不执行分配。
func sliceForAppend(in []byte, n int) (head, tail []byte) {
	if total := len(in) + n; cap(in) >= total {
		head = in[:total]
	} else {
		head = make([]byte, total)
		copy(head, in)
	}
	tail = head[len(in):]
	return
}

// 反密码在计数器模式下使用g.cipher从内到外加密。
func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
	var mask [gcmBlockSize]byte

	for len(in) >= gcmBlockSize {
		g.cipher.Encrypt(mask[:], counter[:])
		gcmInc32(counter)

		xorWords(out, in, mask[:])
		out = out[gcmBlockSize:]
		in = in[gcmBlockSize:]
	}

	if len(in) > 0 {
		g.cipher.Encrypt(mask[:], counter[:])
		gcmInc32(counter)
		xorBytes(out, in, mask[:])
	}
}

// deriveCounter根据给定的nonce计算初始GCM计数器状态。
// 参见NIST SP 800-38D第7.1节。这假设计数器在输入时填充了
// 零。
func (g *gcm) deriveCounter(counter *[gcmBlockSize]byte, nonce []byte) {
	// GCM对于初始计数器
	// 状态有两种操作模式：对于96位（12字节）的nonce，“快速路径”和对于其他长度的nonce的“慢速路径”。对于96位的nonce，nonce和
	// 以及从一开始的四字节big-endian计数器一起直接用作起始计数器。对于其他nonce大小，计数器
	// 通过GHASH函数进行计算。
	if len(nonce) == gcmStandardNonceSize {
		copy(counter[:], nonce)
		counter[gcmBlockSize-1] = 1
	} else {
		var y gcmFieldElement
		g.update(&y, nonce)
		y.high ^= uint64(len(nonce)) * 8
		g.mul(&y)
		binary.BigEndian.PutUint64(counter[:8], y.low)
		binary.BigEndian.PutUint64(counter[8:], y.high)
	}
}

// auth计算GHASH（密文，附加数据），用
// tagMask屏蔽结果，并将结果写入out。
func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]byte) {
	var y gcmFieldElement
	g.update(&y, additionalData)
	g.update(&y, ciphertext)

	y.low ^= uint64(len(additionalData)) * 8
	y.high ^= uint64(len(ciphertext)) * 8

	g.mul(&y)

	binary.BigEndian.PutUint64(out, y.low)
	binary.BigEndian.PutUint64(out[8:], y.high)

	xorWords(out, out, tagMask[:])
}
